#include "contour_extractor.h"
#include "types.h"
#include "image.h"
#include "core.h"
#include <typeinfo>
#include <iostream>

using std::cout;

int ContourExtractor::invertChainCode(int code){
    if(code > 3)
        return code - 4;
    else return code + 4;
}

Position ContourExtractor::findFirstPoint(Binary_Image *image){
    Position point;
    point.y=-1;
    point.x=-1;
    for(unsigned int i = 0; i < image->getHeight(); i++)
        for(unsigned int j = 0; j < image->getWidth() ; j++)
            if(image->getPixel(j,i) == true){
                point.x = j-1;
                point.y = i;
                return point;
            }
    return point;
}

Position ContourExtractor::findFirstPoint(Grey_Image *image,unsigned int label){
    Position point;
    point.y=image->getHeight()+1;
    point.x=image->getWidth()+1;
    for(unsigned int i = 0; i < image->getHeight(); i++)
        for(unsigned int j = 0; j < image->getWidth() ; j++)
            if(image->getPixel(j,i) == label){
                point.x = j-1;
                point.y = i;
                return point;
            }
    return point;
}


Position ContourExtractor::findNextPoint(Binary_Image *image){
    int r;  //iterator
    int d_cp;    // direction: current to previous pixel
    int de, di;
    Position Pe, Pi,Pn;
    Pn=current;
    d_cp = invertChainCode(d_pc);
    for(r = 0; r < 7; r++){
        de = (d_cp + r)%8;
        di = (d_cp + r + 1)%8;
        Pe = current+neighbours[de];
        Pi = current+neighbours[di];
        if(Pi.isInside(-1,-1,width,height) && Pe.isInside(-1,-1,width,height)){
            if((image->getPixel(Pi) == true) && (image->getPixel(Pe) != true)){
                d_cn = de;
                Pn=Pe;
            }
        }
        else{
            if(Pi.isInside(-1,-1,width,height)){
                if(image->getPixel(Pi) == true){
                    d_cn = de;
                    Pn=Pe;
                }
            }
        }
    }
    return Pn;
}

Position ContourExtractor::findNextPoint(Grey_Image *image,unsigned int label){
    int r;  //iterator
    int d_cp;    // direction: current to previous pixel
    int de, di;
    Position Pe, Pi,Pn;
    Pn=current;
    d_cp = invertChainCode(d_pc);
    for(r = 0; r < 7; r++){
        de = (d_cp + r)%8;
        di = (d_cp + r + 1)%8;
        Pe = current+neighbours[de];
        Pi = current+neighbours[di];
        if(Pi.isInside(-1,-1,width,height) && Pe.isInside(-1,-1,width,height)){
            if((image->getPixel(Pi) == label) && (image->getPixel(Pe) != label)){
                d_cn = de;
                Pn=Pe;
            }
        }
        else{
            if(Pi.isInside(-1,-1,width,height)){
                if(image->getPixel(Pi) == label){
                    d_cn = de;
                    Pn=Pe;
                }
            }
        }
    }
    return Pn;
}

Contour* ContourExtractor::contourFollower(Binary_Image *image){
    Contour *contour = new Contour(image);
    first = findFirstPoint(image);
    int paths=0;

    if(first.isPositive()){
        contour->addPoint(first);
        current = first;

        for(int i = 4; i < 8; i = i + (8/neighbourhood)){
            if((first+neighbours[i]).isInside(-1,-1,width,height) && (first+neighbours[(i+1)%8]).isInside(-1,-1,width,height))
                if(((image->getPixel(first+neighbours[i])) != true) && ((image->getPixel(first+neighbours[(i+1)%8])) == true)){
                    if(paths==0){
                        next=first+neighbours[i];
                        d_cn = i;
                        paths++;
                    }
                    else if(paths==1)
                        paths++;
                }
        }

        while(paths){
            current=next;
            contour->addPoint(next);
            d_pc = d_cn;
            next = findNextPoint(image);
            if(first==next)
                paths--;
        }
        return contour;
    }
    else{
        delete contour;
        return NULL;
    }
}

Contour* ContourExtractor::contourFollower(Grey_Image *image,unsigned int label){
    Contour *contour = new Contour(image);
    first = findFirstPoint(image,label);
    int paths=0;

    if(first.isPositive()){
        contour->addPoint(first);
        current = first;

        for(int i = 4; i < 8; i = i + (8/neighbourhood)){
            if((first+neighbours[i]).isInside(-1,-1,width,height) && (first+neighbours[(i+1)%8]).isInside(-1,-1,width,height))
                if(((image->getPixel(first+neighbours[i])) != label) && ((image->getPixel(first+neighbours[(i+1)%8])) == label)){
                    if(paths==0){
                        next=first+neighbours[i];
                        d_cn = i;
                        paths++;
                    }
                    else if(paths==1)
                        paths++;
                }
        }

        while(paths!=0){
            current=next;
            contour->addPoint(next);
            d_pc = d_cn;
            next = findNextPoint(image,label);
            if(next==first)
                paths--;
        }
        return contour;
    }
    else{
        delete contour;
        return NULL;
    }
}

Contour* ContourExtractor::getContour(Grey_Image *image,unsigned int label,int neightbourhood){
    Contour *contour;
    height=image->getHeight();
    width=image->getWidth();
    neighbours = createNeighbours();
    this->neighbourhood = EIGHTNEIGHBOURHOOD;
    contour = contourFollower(image,label);
    contour->setClosed(true);
    contour->setLabel(label);
    contour->setOrigin(1);
    contour->setPixelSize(image->getPixelSize());
    contour->setPixelLenght(contour->points.size());
    return contour;
}

Contour* ContourExtractor::getContour(Binary_Image *image,int neightbourhood){
    Contour *contour;
    height=image->getHeight();
    width=image->getWidth();
    neighbours = createNeighbours();
    this->neighbourhood = neightbourhood;
    contour = contourFollower(image);
    contour->setClosed(true);
    contour->setLabel(1);
    contour->setOrigin(1);
    contour->setPixelSize(image->getPixelSize());
    contour->setPixelLenght(contour->points.size());
    if(neighbours!=NULL)
        delete [] neighbours;
    return contour;
}
